/*
** $Id: lua.c $
** Lua的独立解释器
** See Copyright Notice in lua.h
*/

#define lua_c

#include "lprefix.h"


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <signal.h>

#include "lua.h"

#include "lauxlib.h"
#include "lualib.h"


#if !defined(LUA_PROGNAME)
#define LUA_PROGNAME		"lua"
#endif

#if !defined(LUA_INIT_VAR)
#define LUA_INIT_VAR		"LUA_INIT"
#endif

#define LUA_INITVARVERSION	LUA_INIT_VAR LUA_VERSUFFIX


static lua_State *globalL = NULL;

static const char *progname = LUA_PROGNAME;


#if defined(LUA_USE_POSIX)   /* { */

/*
** Use 'sigaction' when available.
*/
static void setsignal (int sig, void (*handler)(int)) {
  struct sigaction sa;
  sa.sa_handler = handler;
  sa.sa_flags = 0;
  sigemptyset(&sa.sa_mask);  /* do not mask any signal */
  sigaction(sig, &sa, NULL);
}

#else           /* }{ */

#define setsignal            signal

#endif                               /* } */


/*
** Hook set by signal function to stop the interpreter.
*/
static void lstop (lua_State *L, lua_Debug *ar) {
  (void)ar;  /* unused arg. */
  lua_sethook(L, NULL, 0, 0);  /* reset hook */
  luaL_error(L, "interrupted!");
}


/*
** Function to be called at a C signal. Because a C signal cannot
** just change a Lua state (as there is no proper synchronization),
** this function only sets a hook that, when called, will stop the
** interpreter.
*/
static void laction (int i) {
  int flag = LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT;
  setsignal(i, SIG_DFL); /* if another SIGINT happens, terminate process */
  lua_sethook(globalL, lstop, flag, 1);
}


static void print_usage (const char *badoption) {
  lua_writestringerror("%s: ", progname);
  if (badoption[1] == 'e' || badoption[1] == 'l')
    lua_writestringerror("'%s' needs argument\n", badoption);
  else
    lua_writestringerror("unrecognized option '%s'\n", badoption);
  lua_writestringerror(
  "usage: %s [options] [script [args]]\n"
  "Available options are:\n"
  "  -e stat   execute string 'stat'\n"
  "  -i        enter interactive mode after executing 'script'\n"
  "  -l mod    require library 'mod' into global 'mod'\n"
  "  -l g=mod  require library 'mod' into global 'g'\n"
  "  -v        show version information\n"
  "  -E        ignore environment variables\n"
  "  -W        turn warnings on\n"
  "  --        stop handling options\n"
  "  -         stop handling options and execute stdin\n"
  ,
  progname);
}


static void l_message (const char *pname, const char *msg) {
  if (pname) lua_writestringerror("%s: ", pname);
  lua_writestringerror("%s\n", msg);
}


/*
** 检测‘status’是否有错，如果有错，打印在栈顶的错误信息。他假定错误信息是一个字符串，因为他是Lua或‘msghandler’生成的
*/
static int report (lua_State *L, int status) {
  if (status != LUA_OK) {
    const char *msg = lua_tostring(L, -1);
    l_message(progname, msg);
    lua_pop(L, 1);  /* 移除信息 */
  }
  return status;
}


/*
** 用于运行所有代码块时的消息处理器
*/
static int msghandler (lua_State *L) {
  const char *msg = lua_tostring(L, 1);
  if (msg == NULL) {  /* 错误对象不是一个字符串? */
    if (luaL_callmeta(L, 1, "__tostring") &&  /* 它有元方法？ */
        lua_type(L, -1) == LUA_TSTRING)  /* 生成了一个字符串? */
      return 1;
    else
      msg = lua_pushfstring(L, "(error object is a %s value)",
                               luaL_typename(L, 1));
  }
  luaL_traceback(L, L, msg, 1);  /* 追加到标准回溯(traceback) */
  return 1;  /* 返回回溯 */
}


/*
** 'lua_pcall' 的实现(设置了相应的消息处理器和C-signal处理器)，用于运行所有代码块。
*/
static int docall (lua_State *L, int narg, int nres) {
  int status;
  int base = lua_gettop(L) - narg;  /* 函数索引 */
  lua_pushcfunction(L, msghandler);  /* 推送消息处理器 */
  lua_insert(L, base);  /* 放在函数和参数下面 */
  globalL = L;  /* 使 'laction' 可用 */
  setsignal(SIGINT, laction);  /* 设置 C-signal 处理器 */
  status = lua_pcall(L, narg, nres, base);
  setsignal(SIGINT, SIG_DFL); /* 重置 C-signal 处理器 */
  lua_remove(L, base);  /* 从栈中移除消息处理器 */
  return status;
}


static void print_version (void) {
  lua_writestring(LUA_COPYRIGHT, strlen(LUA_COPYRIGHT));
  lua_writeline();
}


/*
** 创建'arg'表，其中存储来自命令行('argv')的所有参数。它应该对齐，以便在索引0处，它有'argv[script]'，是脚本名称。
** 脚本的参数('script'之后的所有内容)都是正索引;其他参数(在脚本名称之前)为负索引。
** 如果没有脚本名称，则假定解释器的名称为基础。(如果没有解释器名称，'script'为-1，因此表大小为零。)
*/
static void createargtable (lua_State *L, char **argv, int argc, int script) {
  int i, narg;
  narg = argc - (script + 1);  /* 正索引数 */
  lua_createtable(L, narg, script + 1);
  for (i = 0; i < argc; i++) {
    lua_pushstring(L, argv[i]);
    lua_rawseti(L, -2, i - script);
  }
  lua_setglobal(L, "arg");
}


static int dochunk (lua_State *L, int status) {
  if (status == LUA_OK) status = docall(L, 0, 0);
  return report(L, status);
}


static int dofile (lua_State *L, const char *name) {
  return dochunk(L, luaL_loadfile(L, name));
}


static int dostring (lua_State *L, const char *s, const char *name) {
  return dochunk(L, luaL_loadbuffer(L, s, strlen(s), name));
}


/*
** 接收了'globname[=modname]'就运行'globname=require(modname)'
*/
static int dolibrary (lua_State *L, char *globname) {
  int status;
  char *modname = strchr(globname, '=');
  if (modname == NULL)  /* 没有显式名称? */
    modname = globname;  /* 模块名等于全局名 */
  else {
    *modname = '\0';  /* 全局名在这儿结束 */
    modname++;  /* 模块名在'='之后 */
  }
  lua_getglobal(L, "require");
  lua_pushstring(L, modname);
  status = docall(L, 1, 1);  /* 'require(modname)' */
  if (status == LUA_OK)
    lua_setglobal(L, globname);  /* globname = require(modname) */
  return report(L, status);
}


/*
** 将'arg'表从1至#arg的内容压入栈中
*/
static int pushargs (lua_State *L) {
  int i, n;
  if (lua_getglobal(L, "arg") != LUA_TTABLE)
    luaL_error(L, "'arg' is not a table");
  n = (int)luaL_len(L, -1);
  luaL_checkstack(L, n + 3, "too many arguments to script");
  for (i = 1; i <= n; i++) {
    lua_rawgeti(L, -i, i);
  }
  lua_remove(L, -i);  /* 删除栈中的表 */
  return n;
}


static int handle_script (lua_State *L, char **argv) {
  int status;
  const char *fname = argv[0];
  if (strcmp(fname, "-") == 0 && strcmp(argv[-1], "--") != 0)
    fname = NULL;  /* stdin */
  status = luaL_loadfile(L, fname);
  if (status == LUA_OK) {
    int n = pushargs(L);  /* 将脚本的参数压入栈 */
    status = docall(L, n, LUA_MULTRET);
  }
  return report(L, status);
}


/* ‘arg'的参数指示符 */
#define has_error	1	/* bad option */
#define has_i		2	/* -i */
#define has_v		4	/* -v */
#define has_e		8	/* -e */
#define has_E		16	/* -E */


/*
** 遍历’argv'中的所有参数，返回一个在运行任何Lua代码前需要的掩码，或者在找到错误参数时返回错误
** 当发生错误，first是错误参数的索引，否则，如果没有程序名first=-1，没有脚本名时first=0或者其他情况first=脚本名称的索引
*/
static int collectargs (char **argv, int *first) {
  int args = 0;
  int i;
  if (argv[0] != NULL) {  /* 这是一个程序名? */
    if (argv[0][0])  /* 不为空? */
      progname = argv[0];  /* 保存 */
  }
  else {  /* 没有程序名 */
    *first = -1;
    return 0;
  }
  for (i = 1; argv[i] != NULL; i++) {  /* 处理参数 */
    *first = i;
    if (argv[i][0] != '-')  /* 没有option(选项)? */
        return args;  /* 停止处理option */
    switch (argv[i][1]) {  /* else check option */
      case '-':  /* '--' */
        if (argv[i][2] != '\0')  /* '--'后有额外字符? */
          return has_error;  /* 无效option */
        *first = i + 1;
        return args;  // 终止对后面参数的处理
      case '\0':  /* '-' */
        return args;
      case 'E':
        if (argv[i][2] != '\0')  /* 额外字符? */
          return has_error;  /* 无效option */
        args |= has_E;
        break;
      case 'W':
        if (argv[i][2] != '\0')  /* 额外字符? */
          return has_error;  /* 无效option */
        break;
      case 'i':
        args |= has_i;  /* (-i 意味着 -v) *//* 下沉 */
      case 'v':
        if (argv[i][2] != '\0')  /* 额外字符? */
          return has_error;  /* 无效option */
        args |= has_v;
        break;
      case 'e':
        args |= has_e;  /* 下沉 */
      case 'l':  /* 所有需要参数的选项 */
        if (argv[i][2] == '\0') {  /* 没有连接参数? */
          i++;  /* 尝试下一个 'argv' */
          if (argv[i] == NULL || argv[i][0] == '-')
            return has_error;  /* 没有下一个参数或下一个参数是另一个选项 */
        }
        break;
      default:  /* 无效option */
        return has_error;
    }
  }
  *first = 0;  /* 没有脚本名 */
  return args;
}


/*
** 处理选项'e'和'l'，其中涉及运行Lua代码，'W'会影响状态。如果某些代码引发错误，则返回0。
*/
static int runargs (lua_State *L, char **argv, int n) {
  int i;
  for (i = 1; i < n; i++) {
    int option = argv[i][1];
    lua_assert(argv[i][0] == '-');
    switch (option) {
      case 'e':  case 'l': {
        int status;
        char *extra = argv[i] + 2;
        if (*extra == '\0') extra = argv[++i];
        lua_assert(extra != NULL);
        status = (option == 'e')
                 ? dostring(L, extra, "=(command line)")
                 : dolibrary(L, extra);
        if (status != LUA_OK) return 0;
        break;
      }
      case 'W':
        lua_warning(L, "@on", 0);  /* 打开警告 */
        break;
    }
  }
  return 1;
}


static int handle_luainit (lua_State *L) {
  const char *name = "=" LUA_INITVARVERSION;
  const char *init = getenv(name + 1);
  if (init == NULL) {
    name = "=" LUA_INIT_VAR;
    init = getenv(name + 1);
  }
  if (init == NULL) return LUA_OK;
  else if (init[0] == '@')
    return dofile(L, init+1);
  else
    return dostring(L, init, name);
}


/*
** {==================================================================
**  读取(Read)-运算(Eval)-输出(Print)-循环(Loop) (REPL，交互式解释器)
** ===================================================================
*/

// 提示符
#if !defined(LUA_PROMPT)
#define LUA_PROMPT		"> "
#define LUA_PROMPT2		">> "
#endif

#if !defined(LUA_MAXINPUT)
#define LUA_MAXINPUT		512
#endif


/*
** lua_stdin_is_tty 表明 标准输入是否为'tty' (也就是说，我们是否交互式运行lua).
*/
#if !defined(lua_stdin_is_tty)	/* { */

#if defined(LUA_USE_POSIX)	/* { */

#include <unistd.h>
#define lua_stdin_is_tty()	isatty(0)

#elif defined(LUA_USE_WINDOWS)	/* }{ */

#include <io.h>
#include <windows.h>

#define lua_stdin_is_tty()	_isatty(_fileno(stdin))

#else				/* }{ */

/* ISO C definition */
#define lua_stdin_is_tty()	1  /* assume stdin is a tty */

#endif				/* } */

#endif				/* } */


/*
** lua_readline 定义显示提示然后从标准输入中读取一行的方法
** lua_saveline 定义了如何在“历史记录”中“保存”读行
** lua_freeline 定义了如何释放lua_readline读取的一行
*/
#if !defined(lua_readline)	/* { */

#if defined(LUA_USE_READLINE)	/* { */

#include <readline/readline.h>
#include <readline/history.h>
#define lua_initreadline(L)	((void)L, rl_readline_name="lua")
#define lua_readline(L,b,p)	((void)L, ((b)=readline(p)) != NULL)
#define lua_saveline(L,line)	((void)L, add_history(line))
#define lua_freeline(L,b)	((void)L, free(b))

#else				/* }{ */

#define lua_initreadline(L)  ((void)L)
#define lua_readline(L,b,p) \
        ((void)L, fputs(p, stdout), fflush(stdout),  /* show prompt */ \
        fgets(b, LUA_MAXINPUT, stdin) != NULL)  /* get line */
#define lua_saveline(L,line)	{ (void)L; (void)line; }
#define lua_freeline(L,b)	{ (void)L; (void)b; }

#endif				/* } */

#endif				/* } */


/*
** 返回将被解释器用作提示符的字符串。将字符串(如果使用默认值，则为nil)保留在堆栈上，以保持其锚定
** firstline表示是否第一行
*/
static const char *get_prompt (lua_State *L, int firstline) {
  if (lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2") == LUA_TNIL)
    return (firstline ? LUA_PROMPT : LUA_PROMPT2);  /* 使用默认值 */
  else {
    const char *p = luaL_tolstring(L, -1, NULL);
    lua_remove(L, -2);  /* 移除原始值 */
    return p;
  }
}

/* mark in error messages for incomplete statements */
#define EOFMARK		"<eof>"
#define marklen		(sizeof(EOFMARK)/sizeof(char) - 1)


/*
** 检测状态是否表示语法错误并且错误信息在栈顶以不完整语句结束
*/
static int incomplete (lua_State *L, int status) {
  if (status == LUA_ERRSYNTAX) {
    size_t lmsg;
    const char *msg = lua_tolstring(L, -1, &lmsg);
    if (lmsg >= marklen && strcmp(msg + lmsg - marklen, EOFMARK) == 0) {
      lua_pop(L, 1);
      return 1;
    }
  }
  return 0;  /* else... */
}


/*
** 提示用户，读取一行，并且将他压入栈
*/
static int pushline (lua_State *L, int firstline) {
  char buffer[LUA_MAXINPUT];
  char *b = buffer;
  size_t l;
  const char *prmt = get_prompt(L, firstline);
  int readstatus = lua_readline(L, b, prmt);
  if (readstatus == 0)
    return 0;  /* 没有输入 (提示将由调用弹出) */
  lua_pop(L, 1);  /* 移除提示符 */
  l = strlen(b);
  if (l > 0 && b[l-1] == '\n')  /* 以新行结束? */
    b[--l] = '\0';  /* 移除它 */
  if (firstline && b[0] == '=')  /* 兼容 5.2, ... */
    lua_pushfstring(L, "return %s", b + 1);  /* 修改 '=' 为 'return' */
  else
    lua_pushlstring(L, b, l);
  lua_freeline(L, b);
  return 1;
}


/*
** 尝试将栈上的行编译为 'return <line>;'; 返回时, 栈要么是已经编译的代码块要么是原始行(编译失败)
*/
static int addreturn (lua_State *L) {
  const char *line = lua_tostring(L, -1);  /* original line */
  const char *retline = lua_pushfstring(L, "return %s;", line);
  int status = luaL_loadbuffer(L, retline, strlen(retline), "=stdin");
  if (status == LUA_OK) {
    lua_remove(L, -2);  /* 移除修改过的行 */
    if (line[0] != '\0')  /* 不为空? */
      lua_saveline(L, line);  /* 保存历史 */
  }
  else
    lua_pop(L, 2);  /* 弹出 'luaL_loadbuffer' 的结果和修改过的行 */
  return status;
}


/*
** 直到出现完整的Lua语句，运行多行
*/
static int multiline (lua_State *L) {
  for (;;) {  /* 重复直到获取一个完整语句 */
    size_t len;
    const char *line = lua_tolstring(L, 1, &len);
    int status = luaL_loadbuffer(L, line, len, "=stdin");  /* try it */
    if (!incomplete(L, status) || !pushline(L, 0)) {
      lua_saveline(L, line);  /* 保存历史 */
      return status;  /* 不能或不应该尝试新行 */
    }
    lua_pushliteral(L, "\n");  /* 添加新行... */
    lua_insert(L, -2);  /* ...两行之间 */
    lua_concat(L, 3);  /* 连接他们 */
  }
}


/*
** 读取一行并尝试首先将其加载(编译)为表达式(通过在其前面添加“return”)，然后将其加载(编译)为语句
**  返回加载/调用的最终状态，以及堆栈顶部的结果函数(如果有的话)
*/
static int loadline (lua_State *L) {
  int status;
  lua_settop(L, 0);
  if (!pushline(L, 1))
    return -1;  /* 没有输入 */
  if ((status = addreturn(L)) != LUA_OK)  /* 'return ...' 不生效? */
    status = multiline(L);  /* 尝试命令，可能需要延续 */
  lua_remove(L, 1);  /* 从栈中移除行 */
  lua_assert(lua_gettop(L) == 1);
  return status;
}


/*
** 打印(调用Lua的’print'函数)所有在栈上的值
*/
static void l_print (lua_State *L) {
  int n = lua_gettop(L);
  if (n > 0) {  /* 有需要打印的结果? */
    luaL_checkstack(L, LUA_MINSTACK, "too many results to print");
    lua_getglobal(L, "print");
    lua_insert(L, 1);
    if (lua_pcall(L, n, 0, 0) != LUA_OK)
      l_message(progname, lua_pushfstring(L, "error calling 'print' (%s)",
                                             lua_tostring(L, -1)));
  }
}


/*
** 运行REPL: 重复read (load)一行, 执行evaluate(call) 它, 打印print所有结果
*/
static void doREPL (lua_State *L) {
  int status;
  const char *oldprogname = progname;
  progname = NULL;  /* 在交互模式中的错误没有‘progname' */
  lua_initreadline(L);
  while ((status = loadline(L)) != -1) {
    if (status == LUA_OK)
      status = docall(L, 0, LUA_MULTRET);
    if (status == LUA_OK) l_print(L);
    else report(L, status);
  }
  lua_settop(L, 0);  /* clear stack */
  lua_writeline();
  progname = oldprogname;
}

/* }================================================================== */


/*
** 独立解释器的主函数 (在保护模式中运行)，读取并处理所有选项
*/
static int pmain (lua_State *L) {
  int argc = (int)lua_tointeger(L, 1);
  char **argv = (char **)lua_touserdata(L, 2);
  int script;

  int args = collectargs(argv, &script);
  int optlim = (script > 0) ? script : argc; /* 第一个参数不是option(’-‘开头) */
  luaL_checkversion(L);  /* 检查解释器版本是否正确 */
  if (args == has_error) {  /* 参数错误? */
    print_usage(argv[script]);  /* 'script' has index of bad arg. */
    return 0;
  }
  if (args & has_v)  /* option '-v'? */
    print_version();
  if (args & has_E) {  /* option '-E'? */
    lua_pushboolean(L, 1);  /* 为库标记忽略环境变量 */
    lua_setfield(L, LUA_REGISTRYINDEX, "LUA_NOENV");
  }
  luaL_openlibs(L);  /* 打开标准库 */
  createargtable(L, argv, argc, script);  /* 创建表'arg' */
  lua_gc(L, LUA_GCRESTART);  /* 开启GC... */
  lua_gc(L, LUA_GCGEN, 0, 0);  /* ...分代模式 */
  if (!(args & has_E)) {  /* no option '-E'? */
    if (handle_luainit(L) != LUA_OK)  /* 运行LUA_INIT */
      return 0;  /* 运行 LUA_INIT 时发生错误 */
  }
  if (!runargs(L, argv, optlim))  /* 执行参数 -e 和 -l */
    return 0;  /* something failed */
  if (script > 0) {  /* 执行主脚本 */
    if (handle_script(L, argv + script) != LUA_OK)
      return 0;  /* 发生错误时中断 */
  }
  if (args & has_i)  /* -i option? */
    doREPL(L);
  else if (script < 1 && !(args & (has_e | has_v))) { /* no active option? */
    if (lua_stdin_is_tty()) {  /* 运行交互模式? */
      print_version();
      doREPL(L);
    }
    else dofile(L, NULL);  /* 将标准输入作为一个文件 */
  }
  lua_pushboolean(L, 1);  /* 标记没有错误 */
  return 1;
}


int main (int argc, char **argv) {
  int status, result;
  lua_State *L = luaL_newstate();  /* 创建state */
  if (L == NULL) {
    l_message(argv[0], "cannot create state: not enough memory");
    return EXIT_FAILURE;
  }
  lua_gc(L, LUA_GCSTOP);  /* 构建状态时关闭GC */
  lua_pushcfunction(L, &pmain);  /* 函数入栈 */
  lua_pushinteger(L, argc);  /* 第一个参数入栈 */
  lua_pushlightuserdata(L, argv); /* 第二个参数入栈 */
  status = lua_pcall(L, 2, 1, 0);  /* 执行调用 */
  result = lua_toboolean(L, -1);  /* 获取结果 */
  report(L, status);
  lua_close(L);
  return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE;
}

